package com.xbongbong.pro.service.toolbox.statistic.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.pojo.CallReckonPojo;
import com.xbongbong.paas.pojo.StageFindPojo;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.performanceset.pojo.PerformanceSetExplainPojo;
import com.xbongbong.pro.service.stage.StageFindStrategy;
import com.xbongbong.pro.service.stage.factory.StageFindFactory;
import com.xbongbong.pro.statistic.enums.ChartTypeEnum;
import com.xbongbong.pro.statistic.pojo.TimePojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultBasePojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPkPojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPojo;
import com.xbongbong.pro.statistic.through.pojo.dto.ThroughDTO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.domain.entity.ContractPerformanceEntity;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerCommunicateEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.QuotationEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.enums.subform.ContractPerformanceSubFormEnum;
import com.xbongbong.saas.enums.subform.ContractProductSubFormEnum;
import com.xbongbong.saas.model.ContractPerformanceModel;
import com.xbongbong.saas.model.DataDictionaryModel;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.join.query.HasParentQueryBuilder;
import org.elasticsearch.join.query.JoinQueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.paas.elasticsearch.constant.EsNormalConstant.MINI_MUM_SHOULD_MATCH;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 用于图表查询条件的封装处理
 * @author zcp
 * @version v1.0
 * @date 2019/4/5 14:50
 * @since v1.0
 */
@Component
public class ChartRuleQueryHelp {

    private static final Logger LOG = LoggerFactory.getLogger(ChartRuleQueryHelp.class);
    @Resource
    private ChartDtoHelp chartDtoHelp;
    @Resource
    private EsHelper esHelper;
    @Resource
    private PerformanceHelp performanceHelp;
    @Resource
    private DataDictionaryModel dataDictionaryModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private ContractPerformanceModel contractPerformanceModel;
    @Resource
    private ThroughHelp throughHelp;

    //-------------------------------所有业务基础查询条件：corpid、del、时间-------------------------------
    /**
     * 最基础的查询条件封装，只限制corpid和del
     * @param corpid 公司id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/5 16:46
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Base(String corpid){
        return boolQueryBuilder4Base(corpid, DelEnum.NORMAL.getDel());
    }
    /**
     * 最基础的查询条件封装，只限制corpid和del（del为null时不限制）
     * @param corpid 公司id
     * @param del 删除标记@see DelEnum，为空时不限制删除条件
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/5/13 21:57
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Base(String corpid, Integer del){
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        if (del != null) {
            boolQueryBuilder.filter(termQuery("del", del));
        }
        return boolQueryBuilder;
    }
    /**
     * 基础的查询条件封装，限制corpid和del，及主键id
     * @param corpid 公司id
     * @param del 如果del为null，则不限制del条件
     * @param dataIdIn 主键id合集
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/9 21:18
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4BaseByIds(String corpid, Integer del, Collection<Long> dataIdIn){
        dataIdIn.add(-1L);
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        if (del != null) {
            boolQueryBuilder.filter(termQuery("del", del));
        }
        boolQueryBuilder.filter(termsQuery("dataId", dataIdIn));
        return boolQueryBuilder;
    }
    /**
     * 生成基础查询条件：时间范围（限制开始和结束时间）、公司id、del
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 时间筛选字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/29 13:55
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4LimitStart(ChartResultBasePojo chartResultBasePojo, String fieldTime){
        //默认限制开始时间
        return boolQueryBuilder4Time(chartResultBasePojo, fieldTime, true);
    }
    /**
     * 生成基础查询条件：时间范围（根据limitStart控制是否限制开始时间）、公司id、del
     * 部分统计场景不限制开始时间，如客户画像等
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 时间筛选字段
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/30 17:02
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Time(ChartResultBasePojo chartResultBasePojo, String fieldTime, boolean limitStart){
        Integer startTime = chartResultBasePojo.getStartTime();
        Integer endTime = chartResultBasePojo.getEndTime();
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        //时间范围
        RangeQueryBuilder rangeQueryBuilder = rangeQuery(fieldTime);
        if (limitStart) {
            //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
            rangeQueryBuilder.gte(startTime);
        }
        boolQueryBuilder.filter(rangeQueryBuilder.lt(endTime));
        return boolQueryBuilder;
    }

    /**
     * 生成基础查询条件：formId、公司id、del、时间范围（根据limitStart控制是否限制开始时间）
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 筛选时间字段，如创建时间、最后联系时间
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws
     * @author zcp
     * @date 2019/4/5 16:52
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Form(ChartResultBasePojo chartResultBasePojo, String fieldTime, boolean limitStart){
        Long formId = chartResultBasePojo.getRefId();
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, limitStart);
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }
        return boolQueryBuilder;
    }

    //-------------------------------客户相关查询条件封装-------------------------------

    /**
     * 新增客户查询基本条件：公司id、del（新增客户包含回收站客户）、创建时间、表单id、客户创建人
     * 注：该方法限制了创建人
     * @param userFlag 是否在查询时限制签订人
     * @param chartResultBasePojo 参数pojo
     * @param fieldTime 时间字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/30 19:04
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder customerQuery4Create(boolean userFlag, ChartEntity chartEntity, ChartResultBasePojo chartResultBasePojo, String fieldTime){
        //新增客户都限制开始时间
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultBasePojo, fieldTime, true);
        //移除del，重新塞入新的条件：新增客户要包含回收站
        esHelper.removeSearchCondition(boolQueryBuilder, "del");
        boolQueryBuilder.filter(termsQuery("del", Arrays.asList(DelEnum.NORMAL.getDel(), DelEnum.DELETE.getDel())));
        //限制创建人id、且非主管或筛选
        if (userFlag && chartDtoHelp.checkJoinRuleIfAll(chartEntity, chartResultBasePojo)) {
            //表本身的createId不能变更，因此在data字典内增加创建人key
            String fieldGroup = CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CREATOR);
            boolQueryBuilder.filter(termsQuery(fieldGroup, chartResultBasePojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    //-------------------------------合同相关查询条件封装-------------------------------

    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime、统计状态、formId、是否红冲
     * @param userFlag 是否在查询时限制签订人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/3/19 20:45
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4LimitStart(boolean userFlag, Integer isRed, ChartEntity chartEntity, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        //默认限制开始时间
        return contractQuery4Base(userFlag, isRed, chartEntity, chartResultBasePojo, true);
    }

    /**
     * 该方法以子为主，即主要查询合同业绩分配
     * 开启业绩分配时，时间和组织架构限制规则为：限制合同的签订时间、业绩分配的分配人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param userFlag 是否在查询时限制分配人
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 15:04
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4performance2Child(Integer isRed, ChartEntity chartEntity, boolean userFlag, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        BoolQueryBuilder performanceQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        //限制分配人id、且非全公司
        if (userFlag && chartDtoHelp.checkJoinRuleIfAll(chartEntity, chartResultBasePojo)) {
            //es结构内，合同业绩分配为合同的子，限制业绩分配的分配人（筛选范围时，对于不在筛选范围内的员工，但是是业绩分配相关人员，也要放进去计算）
            List<String> userIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<Long> contractIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<String> pojoUserIdIn = chartResultBasePojo.getUserIdIn();

            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put(ParameterConstant.CORPID, chartEntity.getCorpid());
            params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            params.put("userIdIn", pojoUserIdIn);
            params.put("negRate", 100);
            if(chartResultBasePojo.getStartTime() !=null && chartResultBasePojo.getEndTime() !=null){
                params.put("startTime", chartResultBasePojo.getStartTime());
                params.put("endTime", chartResultBasePojo.getEndTime());
            }
            List<ContractPerformanceEntity> contractPerformanceEntityList = contractPerformanceModel.findEntitys(params);
            contractPerformanceEntityList.forEach(contractPerformanceEntity -> contractIdIn.add(contractPerformanceEntity.getContractId()));
            // 去重
            List<Long> listWithoutContractIn = contractIdIn.stream().distinct().collect(Collectors.toList());
            //params.remove("userIdIn");
            params.put("contractIdIn", listWithoutContractIn);
            List<ContractPerformanceEntity> list = contractPerformanceModel.findEntitys(params);
            list.forEach(contractPerformanceEntity -> userIdIn.add(contractPerformanceEntity.getUserId()));
            // 去重
            List<String> listWithoutUserIdIn = userIdIn.stream().distinct().collect(Collectors.toList());
            pojoUserIdIn.addAll(listWithoutUserIdIn);
            // 去重
            List<String> joinUserIdIn = pojoUserIdIn.stream().distinct().collect(Collectors.toList());
            performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getEsAttr(ContractPerformanceSubFormEnum.USER_ID), joinUserIdIn));
        }
        /**
         开启业绩分配时，不限制合同的签订人
         把合同相关作为父级条件
         */
        BoolQueryBuilder contractQueryBuilder = contractQuery4LimitStart(false, isRed, chartEntity, chartResultBasePojo);
        // formOpenBase(contractQueryBuilder, chartResultBasePojo, XbbRefTypeEnum.CONTRACT);
        HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery(IndexTypeEnum.IDX_SAAS_CONTRACT.getType(), contractQueryBuilder, false);
        performanceQueryBuilder.filter(hasParentQueryBuilder);
        return performanceQueryBuilder;
    }

    /**
     * 当模版关闭时，过滤关闭的模版
     * @param boolQueryBuilder
     * @param chartResultBasePojo
     * @param xbbRefTypeEnum
     * @return
     * @throws XbbException
     */
    public BoolQueryBuilder formOpenBase(BoolQueryBuilder boolQueryBuilder, ChartResultBasePojo chartResultBasePojo, XbbRefTypeEnum xbbRefTypeEnum) throws XbbException{
        String corpid = chartResultBasePojo.getCorpid();
        if(chartResultBasePojo.getRefId() == null || chartResultBasePojo.getRefId() == 0){
            // 全模版统计时，剔除关闭的模版
            Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            map.put("corpid", corpid);
            map.put("del", 0);
            map.put("businessType", xbbRefTypeEnum.getCode());
            map.put("enable", 1);
            List<PaasFormEntityExt> paasFormEntityExts = paasFormModel.findEntitys(map);
            if (CollectionsUtil.isEmpty(paasFormEntityExts)) {
                return boolQueryBuilder;
            }
            List<Long> formIdList = new ArrayList<>();
            formIdList.add(-1L);
            paasFormEntityExts.forEach(paasFormEntityExt -> formIdList.add(paasFormEntityExt.getId()));
            boolQueryBuilder.filter(termsQuery("formId", formIdList));
        }
        return boolQueryBuilder;
    }


    //-------------------------------跟进记录相关查询条件封装-------------------------------
    /**
     * 所有跟进记录相关统计的共有筛选条件：公司id、del、autoGen、拜访时间
     * @param userFlag 是否在查询时限制创建人list
     * @param chartResultBasePojo 基础参数pojo
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/19 17:00
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder communicateQuery4Base(boolean userFlag, ChartEntity chartEntity, ChartResultBasePojo chartResultBasePojo, boolean limitStart) {
        //时间范围（拜访时间）
        String fieldTime = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_TIME);
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, limitStart);
        //只统计跟进记录类型（当为项目日志时，该值不为0）
        boolQueryBuilder.filter(termQuery("logType", 0));
        //限制创建人id、且非主管或筛选
        if (userFlag && chartDtoHelp.checkJoinRuleIfAll(chartEntity, chartResultBasePojo)) {
            boolQueryBuilder.filter(termsQuery("creatorId.keyword", chartResultBasePojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 限制业务类型，如跟进机会
     * @param boolQueryBuilder 跟进记录查询条件
     * @param xbbRefTypeEnum 业务类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/15 20:47
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder communicateFollowQuery(BoolQueryBuilder boolQueryBuilder, XbbRefTypeEnum xbbRefTypeEnum) {
        //限制业务类型
        String communicateBusinessType = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_BUSINESS_TYPE);
        int businessType = xbbRefTypeEnum.getCode();
        boolQueryBuilder.filter(termQuery(communicateBusinessType, businessType));
        return boolQueryBuilder;
    }

    //-------------------------------机会相关查询条件封装-------------------------------

    /**
     * 新增机会pk查询参数设置
     * @param chartResultPkPojo pk参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/21 10:14
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder opportunityQuery4PkAdd(ChartResultPkPojo chartResultPkPojo){
        String fieldTime = "addTime";
        //阶段：0其他普通类型，1赢单，2输单，3取消，不统计取消
        StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());
        //允许阶段筛选（阶段筛选器）
        Set<String> statisticsCodeIn = stageFindStrategy.findStageCodeList(chartResultPkPojo.getCorpid(), new StageFindPojo(chartResultPkPojo.getRefId(), StageTypeEnum.getExceptCancelType(), chartResultPkPojo.getArgStrIn()));
        /*--------处理查询条件--------*/
        return opportunityQueryBase(chartResultPkPojo, fieldTime, true, statisticsCodeIn);
    }

    //-------------------------------回款相关查询条件封装-------------------------------
    /**
     * paymentSheet相关统计查询共有筛选条件：公司id、del、isBad、回款时间
     * 不限制人员，只限制时间，限制特定回款单类型
     *
     * @param chartResultPojo 参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder paymentQuery4Account(ChartResultPojo chartResultPojo) {
        //筛选回款单类型
        List<String> typeList = PaymentSheetTypeEnum.getCodeList4FundAccount();
        BoolQueryBuilder boolQueryBuilder = paymentQuery4Base(chartResultPojo, typeList);
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.FUND_ACCOUNT), accountIdList));
        }
        return boolQueryBuilder;
    }


    /**
     * paymentSheet相关统计查询（未开启业绩分配情况，回款、坏账、红冲）共有筛选条件：公司id、del、isBad、回款时间
     * 不限制归属人
     *
     * @param chartResultBasePojo 基础参数pojo
     * @param typeList            类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/20 21:01
     * @version v1.0
     * @since v1.0
     */
    public BoolQueryBuilder paymentQuery4Base(ChartResultBasePojo chartResultBasePojo, List<String> typeList) {
        //时间筛选
        String fieldTime = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultBasePojo, fieldTime);
        //处理类型
        String typeAttr = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_SHEET_TYPE);
        boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        //不拿子回款单
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 回款单查询条件（合同业绩分配）
     *
     * @param chartResultBasePojo 基础参数pojo
     * @param typeList 类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/12/12 9:22
     * @since v1.0
     */
    public BoolQueryBuilder paymentPerformanceQuery(ChartResultBasePojo chartResultBasePojo, List<String> typeList) {
        BoolQueryBuilder paymentSheetQuery = paymentQuery4Base(chartResultBasePojo, typeList);
        esHelper.removeSearchCondition(paymentSheetQuery, FieldTypeEnum.SHEET_ALONE.getAlias());
        paymentSheetQuery.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotParentCodeList()));
        return paymentSheetQuery;
    }


    //-------------------------------发票相关查询条件封装-------------------------------


    //-------------------------------产品相关查询条件封装-------------------------------
    /**
     * 获取合同产品查询条件
     * @param corpid 公司id
     * @param contractIdIn 关联合同id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/21 21:24
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder productContractQuery(String corpid, Collection<Long> contractIdIn) {
        //此时的contractIdIn只限制了签订时间（非pk榜的还会限制组织架构）
        contractIdIn.add(-1L);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termsQuery("contractId", contractIdIn));
        return boolQueryBuilder;
    }

    /**
     * 增加一个关于产品id获取合同产品查询条件
     * @param corpid 公司id
     * @param contractIdIn 关联合同id
     * @param chartResultPkPojo 与图表有关的实体
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author wangtengwei
     * @date 2020/06/18 15:24
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder newProductContractQuery(String corpid, Collection<Long> contractIdIn, ChartResultPkPojo chartResultPkPojo) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termsQuery(ContractProductSubFormEnum.getEsAttr(ContractProductSubFormEnum.CONTRACT_ID), contractIdIn));
        if (chartResultPkPojo.getPerformancePk()) {
            PerformanceSetExplainPojo explainPojo = performanceHelp.getPerformanceSetExplainPojo(chartResultPkPojo.getChartEntity());
            List<Long> assessProductList = explainPojo.getAccessProductIds();
            //如果业绩目标，则过滤设置过的考核产品
            if (assessProductList != null && assessProductList.size() > 0) {
                boolQueryBuilder.filter(termsQuery(ContractProductSubFormEnum.getEsAttr(ContractProductSubFormEnum.PRODUCT_ID), assessProductList));
            }
        }
        return boolQueryBuilder;
    }

    /**
     * 产品成本的查询参数
     *
     * @param chartResultPojo corpid
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/31 10:49
     * @since v1.0
     */
    public BoolQueryBuilder productCostQuery(ChartResultPojo chartResultPojo) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultPojo.getCorpid());
        String parentId = ProductEnum.getEsAttr4Keyword(ProductEnum.PARENT_ID);
        //父产品id不为0
        boolQueryBuilder.mustNot(termQuery(parentId, BasicConstant.ZERO));
        return boolQueryBuilder;
    }

    //-------------------------------入库相关查询条件封装-------------------------------

    /**
     * 入库单查询参数，某些关联单据的入库单查询条件，可根据typeIn查询不同的入库类型单据
     *
     * @param chartResultPojo 基础参数
     * @param refIdSet        关联单据id集合
     * @param typeIn          入库类型合集
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/30 13:39
     * @since v1.0
     */
    public BoolQueryBuilder instockQueryByIds4Type(ChartResultPojo chartResultPojo, Set<Long> refIdSet, List<Integer> typeIn) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultPojo.getCorpid());
        //入库类型-生产成品入库
        String type = InstockEnum.getEsAttr4Keyword(InstockEnum.TYPE);
        boolQueryBuilder.filter(termsQuery(type, typeIn));
        //关联单据
        String refId = InstockEnum.getEsAttr4Keyword(InstockEnum.REF_ID);
        boolQueryBuilder.filter(termsQuery(refId, refIdSet));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    //-------------------------------出库相关查询条件封装-------------------------------


    /**
     * 出库单参数（合同id筛选）
     *
     * @param corpid 公司id
     * @param contractIdSet 合同id集合
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/4/28 15:08
     * @since v1.0
     */
    public BoolQueryBuilder outstockQuery4ContractId(String corpid, Set<Long> contractIdSet) {
        //出库类型
        String type = OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE);
        //关联单据
        String refId = OutstockEnum.getEsAttr4Keyword(OutstockEnum.REF_ID);
        BoolQueryBuilder outstockQuery = boolQueryBuilder4Base(corpid);
        outstockQuery.filter(termQuery(type, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        outstockQuery.filter(termsQuery(refId, contractIdSet));
        //无需审批和通过
        outstockQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return outstockQuery;
    }

    //-------------------------------工单相关查询条件封装-------------------------------


    //-------------------------------呼叫中心相关-------------------------------



    //-------------------------------资金账户相关-------------------------------
    /**
     * 资金账户收支情况，流水表查询条件
     * @param chartResultPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder fundAccountFlowQuery(ChartResultPojo chartResultPojo) {
        //单据时间
        String fieldTime = "sheetTime";
        //corpid,del,time
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //账户列表
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery("accountId", accountIdList));
        }
        return boolQueryBuilder;
    }

    /**
     * 获取当页之前所有数据的汇总
     * @param chartResultPojo
     * @param firstSheetTime 某页第一条流水的sheetTime
     * @param firstFlowId 某页第一条流水的flowId
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder fundAccountFlowQuery4PageFirstBefore(ChartResultPojo chartResultPojo, Long firstSheetTime, Long firstFlowId) {
        //单据时间
        //corpid,del,accountId，flowId小于
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        //账户列表
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery("accountId", accountIdList));
        }
        /*
         sheetTime小于firstSheetTime
         or
         sheetTime与firstSheetTime相等，且流水id小于该页第一个firstFlowId
         */
        BoolQueryBuilder oneQueryBuilder = boolQuery();
        BoolQueryBuilder twoQueryBuilder = boolQuery();
        oneQueryBuilder.filter(rangeQuery("sheetTime").lt( firstSheetTime));
        twoQueryBuilder.filter(termQuery("sheetTime", firstSheetTime));
        twoQueryBuilder.filter(rangeQuery(BusinessConstant.ID).lt( firstFlowId));
        boolQueryBuilder.should(oneQueryBuilder);
        boolQueryBuilder.should(twoQueryBuilder);
        boolQueryBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
        return boolQueryBuilder;
    }


    /**
     * 呼叫统计（单个员工）
     *
     * @param callReckonPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/26 16:26
     * @since v1.0
     */
    public BoolQueryBuilder callReckonQuery(CallReckonPojo callReckonPojo) {
        //限制基础条件：corpid、del
        BoolQueryBuilder callQuery = boolQueryBuilder4Base(callReckonPojo.getCorpid());
        //时间
        Integer startTime = callReckonPojo.getStartTime();
        Integer endTime = callReckonPojo.getEndTime();
        String timeAttr = callReckonPojo.getTimeAttr();
        callQuery.filter(rangeQuery(timeAttr).gte(startTime).lt(endTime));
        //用户
        String userId = callReckonPojo.getUserId();
        callQuery.filter(termsQuery("userId.keyword", userId));
        //通话类型
        Integer type = callReckonPojo.getType();
        callQuery.filter(termQuery("type", type));
        return callQuery;
    }

    /**
     * 呼叫统计（单个员工）(筛选是否成功)
     *
     * @param callReckonPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/27 10:14
     * @since v1.0
     */
    public BoolQueryBuilder callSuccessQuery(CallReckonPojo callReckonPojo) {
        BoolQueryBuilder callQuery = callReckonQuery(callReckonPojo);
        Integer result = callReckonPojo.getResult();
        callQuery.filter(termQuery("result", result));
        return callQuery;
    }

    //-------------------------------报价单相关查询条件封装-------------------------------
    /**
     * 报价单数量参数
     *
     * @param basePojo 图表基础参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/15 16:48
     * @since v1.0
     */
    public BoolQueryBuilder quotationCountQuery(ChartResultBasePojo basePojo, ChartEntity chartEntity) {
        return quotationQuery4Base(basePojo, chartEntity);
    }


    /**
     * 根据是否是PK榜拿不同的回款单类型
     *
     * @param chartResultPojo 参数，判断是否是PK
     * @return java.util.List<java.lang.String>
     * @author 徐俊杰
     * @date 2019/12/27 14:52
     * @since v1.0
     */
    public List<String> getCodeListForPaymentSheet(ChartResultPojo chartResultPojo) {
        Boolean performancePk = chartResultPojo.getPerformancePk();
        if (performancePk) {
            return PaymentSheetTypeEnum.getCodeListForPK();
        } else {
            return PaymentSheetTypeEnum.getNotBadCodeList();
        }
    }

    /**
     * 自定义指标穿透
     * @param chartEntity
     * @param chartResultBasePojo
     * @return java.lang.String
     * @throws XbbException
     * @author xinpeng.jiang
     * @date 2021/5/12 10:10
     * @version 1.0
     */
    public String getThrough(ChartEntity chartEntity, ChartResultBasePojo chartResultBasePojo) {
        JSONObject driverSourcesObject = chartEntity.getDriverSources();
        Integer businessType = driverSourcesObject.getInteger("businessType");
        Integer saasMark = driverSourcesObject.getInteger("saasMark");
        ThroughDTO throughDTO = new ThroughDTO(chartResultBasePojo);
        JSONObject baseThrough = throughDTO.getBaseThrough();
        baseThrough.put("chartType", chartEntity.getChartType());
        baseThrough.put("saasMark", saasMark);
        baseThrough.put("statisticsType", chartEntity.getStatisticsType());
        baseThrough.put("resultCount", chartResultBasePojo.getCount());
        // 自定义pk不需要businessType
        if (!Objects.equals(chartEntity.getChartType(), ChartTypeEnum.PK_CUSTOM.getCode())) {
            baseThrough.put("businessType", businessType);
            //所有客户和公海池客户穿透 需要分开 通过不同businessType 判断
            if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                JSONArray filter = chartEntity.getFilter();
                if (CollectionUtils.isNotEmpty(filter)) {
                    for (int i = 0; i < filter.size(); i++) {
                        JSONObject filterJson = filter.getJSONObject(i);
                        JSONArray jsonArray = filterJson.getJSONArray("value");
                        if (jsonArray != null) {
                            if (Objects.equals(filterJson.getString("attr"), CustomerManagementEnum.IS_PUBLIC.getAttr())
                                    && Objects.equals(jsonArray.get(0), BasicConstant.ONE)) {
                                baseThrough.put("businessType", XbbRefTypeEnum.CUSTOMER_PUBLIC.getCode());
                            }
                        }
                    }
                }
            }
        }
        String through = "";
        if (!businessType.equals(XbbRefTypeEnum.COMMUNICATE_PLAN.getCode())
                && !businessType.equals(XbbRefTypeEnum.WORKREPORT_DAILY.getCode())
                && !businessType.equals(XbbRefTypeEnum.WORKREPORT_MONTHLY.getCode())
                && !businessType.equals(XbbRefTypeEnum.WORKREPORT_WEEKLY.getCode())) {
            TimePojo timePojo = throughHelp.formatThroughTimeInfo(false, chartResultBasePojo.getFiscalYearTimePojo(), chartResultBasePojo.getStartTime(), chartResultBasePojo.getEndTime());
            through = throughHelp.getThrough(throughDTO, timePojo);
        }
        return through;
    }

    //-------------------------------private方法-------------------------------

    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime（根据limitStart确定是否限制开始时间）、统计状态、formId、是否红冲
     * @param userFlag 是否在查询时限制签订人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param chartResultBasePojo 基础参数pojo
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/3/30 19:18
     * @since v1.0
     * @version v1.0
     */
    private BoolQueryBuilder contractQuery4Base(boolean userFlag, Integer isRed, ChartEntity chartEntity, ChartResultBasePojo chartResultBasePojo, boolean limitStart) throws XbbException {
        String corpid = chartResultBasePojo.getCorpid();
        //时间筛选
        String fieldTime = ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_TIME);
        //调用模板筛选的基础方法
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultBasePojo, fieldTime, limitStart);
        //只获取参与统计的合同状态---合同状态不限制formId
        List<String> statusIn = dataDictionaryModel.statisticTypeList(DictionaryEnum.CONTRACT_STATUS, corpid, null);
        boolQueryBuilder.filter(termsQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.STATUS), statusIn));
        //限制签订人id、且非主管或筛选
        if (userFlag && chartDtoHelp.checkJoinRuleIfAll(chartEntity, chartResultBasePojo)) {
            boolQueryBuilder.filter(termsQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_PERSON), chartResultBasePojo.getUserIdIn()));
        }
        if (isRed != null) {
            String isRedAttr = ContractEnum.getEsAttr4Keyword(ContractEnum.IS_RED);
            boolQueryBuilder.filter(termQuery(isRedAttr, isRed));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }
    
    /**
     * 所有机会相关统计的共有筛选条件：公司id、del、时间(字段从外部传入)、阶段
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 筛选时间字段，如创建时间、预计结束时间
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @param saleStageCodeIn 阶段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/19 21:34
     * @since v1.0
     * @version v1.0
     */
    private BoolQueryBuilder opportunityQueryBase(ChartResultBasePojo chartResultBasePojo, String fieldTime, boolean limitStart, Set<String> saleStageCodeIn){
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, limitStart);
        // formId表单模版
        Long formId = chartResultBasePojo.getRefId();
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }
        //阶段id
        if (CollectionUtils.isNotEmpty(saleStageCodeIn)) {
            boolQueryBuilder.filter(termsQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE), saleStageCodeIn));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 报价单基础ES查询条件
     *
     * @param basePojo 基础pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/15 15:55
     * @since v1.0
     */
    private BoolQueryBuilder quotationQuery4Base(ChartResultBasePojo basePojo, ChartEntity chartEntity) {
        //时间筛选
        String fieldTime = QuotationEnum.getEsAttr4Keyword(QuotationEnum.QUOTATION_TIME);
        //基本条件corpid,del,time
        BoolQueryBuilder quotationQuery = boolQueryBuilder4Time(basePojo, fieldTime, true);
        //报价人筛选
        if (chartDtoHelp.checkJoinRuleIfAll(chartEntity, basePojo)) {
            quotationQuery.filter(termsQuery(QuotationEnum.getEsAttr4Keyword(QuotationEnum.QUOTATION_PERSON), basePojo.getUserIdIn()));
        }
        //todo 用枚举
        quotationQuery.filter(termQuery(QuotationEnum.getEsAttr4Keyword(QuotationEnum.IS_NEW), 1));
        //todo 用枚举
        quotationQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return quotationQuery;
    }

    /**
     * 处理红冲和作废发票
     *
     * @param isCancel 是否作废 null不限制该条件，1作废0否
     * @param isRed 是否红冲 null不限制该条件，1红冲0否
     * @param boolQueryBuilder 查询条件
     * @author 徐俊杰
     * @date 2019/11/27 15:44
     * @since v1.0
     */
    private void dealInvoiceRedCancel(Integer isCancel, Integer isRed, BoolQueryBuilder boolQueryBuilder) {
        if (isCancel != null) {
            //isBad字段
            String isCancelAttr = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.IS_CANCEL);
            //坏账or非坏账
            boolQueryBuilder.filter(termQuery(isCancelAttr, isCancel));
        }
        if (isRed != null) {
            //isRed字段
            String isRedAttr = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.IS_RED);
            //红冲or非红冲
            boolQueryBuilder.filter(termQuery(isRedAttr, isRed));
        }
    }

}
